package gu.sql2java.excel.config;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.base.Strings.nullToEmpty;
import static net.gdface.utils.MiscellaneousUtils.elementsOf;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.ChronoLocalDateTime;
import java.util.Date;
import java.util.List;

import org.apache.commons.beanutils.MorePropertyDescriptor;
import org.apache.commons.beanutils.MorePropertyUtilsBean;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;

import com.google.common.base.Throwables;
import com.google.common.primitives.Primitives;

import gu.sql2java.excel.annotations.ExcelColumn;

public class ExcelPropertyConfig implements PropertyConfig {	
	private ColumnConfig columnConfig;
    private SheetConfig parent;
    private PropertyUtilsBean propertyUtils = new MorePropertyUtilsBean();
    /** .分割的字段在输出字段树的全路径节点名 */
    private String nestedName;
	public ExcelPropertyConfig(ColumnConfig columnConfig) {
		this.columnConfig = checkNotNull(columnConfig,"columnConfig is null");
		checkArgument(!isNullOrEmpty(columnConfig.getColumnName()),"ColumnConfig#columnName is null or empty");
		this.nestedName = columnConfig.getColumnName();
	}
	public ExcelPropertyConfig(ColumnConfig columnConfig,String nestedName) {
		this.columnConfig = checkNotNull(columnConfig,"columnConfig is null");
		setNestedName(nestedName);
	}
	public ExcelPropertyConfig(ExcelColumn excelColumn) {
		this(new ColumnConfig(excelColumn));
	}
	public ExcelPropertyConfig(ExcelColumn excelColumn, String nestedName) {
		this(new ColumnConfig(excelColumn),nestedName);
	}
	/**
	 * 构造方法<br>
	 * 先根据read/write方法定义 ExcelColumn 注解来获取excel导出定义
	 * @param descriptor
	 * @param nestedName
	 */
	public ExcelPropertyConfig(PropertyDescriptor descriptor,String nestedName) {
		this(createColumnConfig(descriptor),nestedName);
	}

	public ExcelPropertyConfig(Field field,String nestedName) {
		this(new ColumnConfig(field.getAnnotation(ExcelColumn.class)),checkNotNull(field,"field is null").getName());		
		setNestedName(nestedName);
	}
	public ExcelPropertyConfig(String nestedName) {
		this(new ColumnConfig(columnNameof(nestedName)),nestedName);
	}
	public ExcelPropertyConfig(String nestedName,String name) {
		this(new ColumnConfig(columnNameof(nestedName),name),nestedName);
	}
	private static String columnNameof(String nestedName){
		List<String> list = elementsOf(nullToEmpty(nestedName),".");
		checkArgument(!list.isEmpty(),"INVALID nestedName %s",nestedName);
		return list.get(list.size()-1);
	}
	@Override
	public ColumnConfig getColumnConfig() {
		return columnConfig;
	}

	@Override
	public void setColumnConfig(ColumnConfig columnConfig) {
		if(null != columnConfig){
			this.columnConfig = columnConfig;
		}
	}
	@Override
	public String getNestedName() {
		return nestedName;
	}

	@Override
	public void setNestedName(String nestedName) {
		if(!isNullOrEmpty(nestedName)){
			this.nestedName = nestedName;
		}
	}
	@Override
	public void setParent(SheetConfig parentConfig) {
		this.parent = parentConfig;
		propertyUtils = new MorePropertyUtilsBean(parent::configOf);
	}

	@Override
	public Object readFrom(Object from) {
		try {
			return  null == from ? null : propertyUtils.getProperty(from, getNestedName());			
		} catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}

	@Override
	public void writeTo(Object to, Object value) {		
		try {
			if(null != to){
				propertyUtils.setProperty(to, getNestedName(),value);
			}
		} catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}

	@Override
	public String getExcelColumnName(){
		String name = columnConfig.getName();
		if(name.isEmpty()){
			name = columnConfig.getColumnName();
			if(name.isEmpty()){
				name = nestedName;
			}
		}
		return name;
	}
    public String getDataFormat(Class<?>type){
    	String dataFormat = "";
    	if(null == type){
    		return dataFormat;
    	}
    	type = Primitives.wrap(type);
    	if(Date.class.isAssignableFrom(type)|| ChronoLocalDate.class.isAssignableFrom(type) || ChronoLocalDateTime.class.isAssignableFrom(type)){
    		dataFormat = columnConfig.getDateFormat();
    	}else if (Long.class.equals(type) || Integer.class.equals(type)|| Short.class.equals(type)|| Byte.class.equals(type)) {
    		dataFormat = columnConfig.getIntegralFormat();
		}
    	if(!dataFormat.isEmpty()){
    		return dataFormat;
    	}
    	/** 如果  ColumnConfig 没有定义数据格式,则根据类型从 SheetConfig中获取格式 */
    	if(Time.class.isAssignableFrom(type)){
    		return parent.getTimeFormat();
		}else if(java.sql.Date.class.isAssignableFrom(type)){
    		return parent.getDateFormat();
    	}else if(Date.class.isAssignableFrom(type)){
    		return parent.getDateTimeFormat();
    	}else if(LocalDate.class.isAssignableFrom(type)){
    		return parent.getDateFormat();
    	}else if(LocalDateTime.class.isAssignableFrom(type)){
    		return parent.getDateTimeFormat();
    	}else if(Timestamp.class.isAssignableFrom(type)){
    		return parent.getTimestampFormat();
    	}else if(Long.class.equals(type) || Integer.class.equals(type)|| Short.class.equals(type)|| Byte.class.equals(type)){
    		return parent.getIntegralFormat();
    	}
    	return dataFormat;
    }
    @Override
	public String getDataFormat(Object value){
    	return null == value ? "" : getDataFormat(value.getClass());
    }
	private static ColumnConfig createColumnConfig(PropertyDescriptor descriptor){
		checkArgument(null != descriptor ,"INVALID descriptor");
		/** MorePropertyDescriptor 实现类提供的Read/Write方法不是bean定义的方法 */
		if(!(descriptor instanceof MorePropertyDescriptor)){
			Method readMethod = descriptor.getReadMethod();
			if(null != readMethod){
				return new ColumnConfig(readMethod.getAnnotation(ExcelColumn.class),descriptor.getName());
			}
			Method writeMethod = descriptor.getWriteMethod();
			if(null != writeMethod){
				return new ColumnConfig(writeMethod.getAnnotation(ExcelColumn.class),descriptor.getName());
			}
		}
		return new ColumnConfig(descriptor.getName());
	}
	
	
	@Override
	public IndexedColors getColor() {
		IndexedColors c = columnConfig.getColor();
		return null == c? parent.getFontColor(): c; 
	}
	@Override
	public IndexedColors getFillColor() {
		IndexedColors c = columnConfig.getFillColor();
		return null == c? parent.getFillColor(): c; 
	}
	@Override
	public HorizontalAlignment getHorizontalAlign() {
		HorizontalAlignment align = columnConfig.getHorizontalAlign();
		return null == align ? parent.getHorizontalAlign() : columnConfig.getHorizontalAlign();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((nestedName == null) ? 0 : nestedName.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ExcelPropertyConfig other = (ExcelPropertyConfig) obj;
		if (nestedName == null) {
			if (other.nestedName != null)
				return false;
		} else if (!nestedName.equals(other.nestedName))
			return false;
		return true;
	}
	@Override
	public String toString() {
		return "ExcelPropertyConfig [nestedName=" + nestedName + "]";
	}
	
}
